/*
 * @Author: AskMeWhy
 * @Date: 2020-10-26 10:51:16
 * @Last Modified by: AskMeWhy
 * @Last Modified time: 2021-01-11 18:31:13
 */
import { merge, ObserverClass } from "@/utils";
export class AskWebSocket extends ObserverClass {
    instance: any = null;
    url = "";
    private reconnectionStep = 0;
    private state: any = {
        type: "",
        text: "",
        value: ""
    };

    private timeout = 6000;
    private reconnection: any = {
        num: 10,
        timeout: 10 * 60 * 1000,
        interval: 1000,
        allow: true
    }

    private heart: any = {
        allow: false,
        interval: 1000,
        timeout: 2000
    }

    constructor(params: any) {
        super(params);
        this.buildParam(params);
        this.init();
    }

    // 构建参数
    private buildParam(params: any) {
        let option = {
            url: "",
            timeout: 6000,
            reconnection: {
                num: 10,
                timeout: 10 * 60 * 1000,
                interval: 1000,
                allow: true
            },
            heart: {
                allow: false,
                interval: 1000,
                timeout: 2000
            }
        };
        option = merge(true, option, params);

        merge(true, this, {
            url: option.url,
            timeout: option.timeout,
            reconnection: {
                num: option.reconnection.num,
                timeout: option.reconnection.timeout,
                interval: option.reconnection.interval,
                allow: option.reconnection.allow
            },
            heart: {
                allow: option.heart.allow,
                interval: option.heart.interval,
                timeout: option.heart.timeout,
                timeoutTimer: null,
                intervalTimer: null
            }
        });
    }

    private init() {
        this.connectionSocket();
    }

    private connectionSocket() {
        try {
            this.instance = new WebSocket(this.url);
            this.handleSocket();
        } catch (e) {
            // statements
            console.log("浏览器不支持websocket!");
        }
    }

    private setState(opt: any) {
        const { state } = this;
        merge(true, state, opt);
    }

    private handleMessage(opt: any) {
        console.log("===================start===================");
        console.log(this.url + ":   ", opt.message);
        if (opt.data) {
            console.log("data:   ", opt.data);
        }
        console.log("===================end===================");
    }

    private handleSocket() {
        const { instance } = this;
        instance.onopen = () => {
            // if (this.state.value === -1) {
            //     return;
            // }
            this.setState({
                value: 0,
                text: "服务器已连接",
                type: "CONNECTION"
            });
            this.emit("connection", this.state);
            this.sendHeart();
            this.handleMessage({ message: this.state.text });
        };
        instance.onmessage = (e: any) => {
            let data: any = {};
            try {
                data = JSON.parse(e.data);
            } catch (error) {
                if (error.cancel) {
                    return;
                }
                data = e.data;
            }
            if (data.type && data.type === "data") {
                this.emit("message", data.data);
                return;
            }
            if (data.type && data.type === "ping") {
                this.receiveHeart();
                return;
            }
            this.emit("message", data);
        };
        instance.onclose = (e: any) => {
            this.resetHeart();
            if (this.state.value !== -1) {
                this.setState({
                    value: 3,
                    text: "服务器已关闭",
                    type: "CLOSE"
                });
                this.emit("close", e);
            } else {
                this.setState({
                    value: -2,
                    text: "服务器重连失败",
                    type: "RECONNECT_ERROR"
                });
                this.emit("reconnectError", e);
            }
            this.handleMessage({ message: this.state.text });
            this.reconnectSocket();
        };
        instance.onerror = function(e: any) {
            this.resetHeart();
            this.setState({
                value: 4,
                text: "服务器出错",
                type: "ERROR"
            });
            this.emit("error", e);
            this.handleMessage({ message: this.state.text });
        };
    }

    private reconnectSocket() {
        const { reconnectionStep, reconnection, state } = this;
        if (!reconnection.allow) {
            return;
        }
        if (state.value === 0 || reconnectionStep === reconnection.num) {
            this.handleMessage({
                message: "停止重连"
            });
            return;
        }
        this.reconnectionStep++;
        this.setState({
            value: -1,
            text: "服务器重连中",
            type: "RECONNECT"
        });
        this.emit("reconnect", this.state);
        const timer = setTimeout(() => {
            this.handleMessage({
                message:
                    "尝试重连中，当前重连次数为：" +
                    this.reconnectionStep +
                    "/" +
                    reconnection.num
            });
            this.connectionSocket();
            clearTimeout(timer);
        }, reconnection.interval);
    }

    sendMessage(opt: any) {
        const { instance } = this;
        if (instance.readyState === WebSocket.OPEN) {
            this.handleMessage({
                message: "客服端发送消息：",
                data: opt
            });

            instance.send(JSON.stringify(opt));
        } else {
            this.handleMessage({
                message: "websocket暂未连接",
                data: opt
            });
        }
    }

    private resetHeart() {
        const { heart } = this;
        heart.intervalTimer && clearTimeout(heart.intervalTimer);
        heart.timeoutTimer && clearTimeout(heart.timeoutTimer);
    }

    private sendHeart() {
        const { heart, instance } = this;
        if (!heart.allow) {
            return;
        }
        heart.intervalTimer = setTimeout(() => {
            this.sendMessage({
                type: "ping"
            });
            this.emit("heart", instance);
            heart.timeoutTimer = setTimeout(() => {
                instance.close();
                this.resetHeart();
            }, heart.timeout);
        }, heart.interval);
    }

    private receiveHeart() {
        this.reconnectionStep = 0;
        this.resetHeart();
        this.sendHeart();
    }
}
